package com.example.lin.controller;

import com.opencsv.CSVWriter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @className: OpenCsvController
 * @description: opencsv 文件处理 控制器
 * @author: Lin.Gu
 * @date: 2025-09-29  13:51
 */
@RestController
@RequestMapping("/opencsv")
@Slf4j
public class OpenCsvController {

    @GetMapping("/download/csv")
    public void downloadCsv(HttpServletResponse response) {
        // 1. 模拟数据（实际场景可能从数据库查询）
        List<String[]> data = new ArrayList<>();
        data.add(new String[]{"姓名", "年龄", "邮箱", "性别"});
        data.add(new String[]{"张三", "25", "zhangsan@example.com", "男"});
        data.add(new String[]{"李四", "30", "lisi@example.com", "女"});
        data.add(new String[]{"王五", "28", "wangwu@example.com", "男"});
        data.add(new String[]{"赵六", "35", "zhaoliu@example.com", "男,女"});

        // 2. 设置响应头（兼容多浏览器）
        response.setContentType("text/csv; charset=UTF-8");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0); // 禁用缓存

        try {
            // 处理文件名编码（兼容IE和主流浏览器）
            String fileName = "用户数据.csv";
            String encodedFileName = URLEncoder.encode(fileName, "UTF-8")
                    .replaceAll("\\+", "%20"); // 替换空格为%20
            response.setHeader("Content-Disposition",
                    "attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName);

            // 3. 资源自动关闭（将OutputStreamWriter纳入try-with-resources）
            try (OutputStreamWriter osw = new OutputStreamWriter(
                    response.getOutputStream(), StandardCharsets.UTF_8);
                 CSVWriter writer = new CSVWriter(osw)) {

                // 写入UTF-8 BOM头（解决Excel乱码）
                osw.write('\uFEFF');
                osw.flush();

//                // 4. 分批写入（适合大数据量，此处示例直接写入，实际可拆分）
//                for (String[] row : data) {
//                    writer.writeNext(row);
//                }

                // 4. 批量写入（适合大数据量，提高写入效率）
                writer.writeAll(data);

                // 手动刷新，确保数据全部输出
                writer.flush();
                log.info("CSV文件导出成功，数据行数：{}", data.size());
            }

        } catch (Exception e) {
            // 5. 异常处理：记录日志+返回前端错误提示
            log.error("CSV文件导出失败", e);
            try {
                response.setContentType("text/plain; charset=UTF-8");
                response.getWriter().write("文件导出失败，请稍后重试");
            } catch (Exception ex) {
                log.error("错误信息写入响应流失败", ex);
            }
        }
    }


    /**
     * 多CSV文件打包下载（保留 OpenCSV 的 writer.writeAll(data) 批量写入）
     */
    @GetMapping("/multi-csv-zip")
    public void downloadMultiCsvZip(HttpServletResponse response) {
        // 1. 准备多组CSV数据（实际场景可从数据库查询）
        // 第一组：用户数据
        List<String[]> userData = new ArrayList<>();
        userData.add(new String[]{"姓名", "年龄", "邮箱", "性别"});
        userData.add(new String[]{"张三", "25", "zhangsan@example.com", "男"});
        userData.add(new String[]{"李四", "30", "lisi@example.com", "女"});
        userData.add(new String[]{"赵六", "35", "zhaoliu@example.com", "男,女"}); // 含逗号

        // 第二组：商品数据
        List<String[]> productData = new ArrayList<>();
        productData.add(new String[]{"商品ID", "名称", "价格", "库存"});
        productData.add(new String[]{"P001", "手机", "5999", "100"});
        productData.add(new String[]{"P002", "电脑", "8999", "50"});

        // 第三组：订单数据
        List<String[]> orderData = new ArrayList<>();
        orderData.add(new String[]{"订单号", "用户ID", "金额", "状态"});
        orderData.add(new String[]{"O001", "U1001", "12999", "已支付"});
        orderData.add(new String[]{"O002", "U1002", "8999", "待发货"});

        // 2. 响应头配置（ZIP格式+禁用缓存+中文文件名兼容）
        response.setContentType("application/zip"); // 移除charset，避免响应头冲突
        response.setHeader(HttpHeaders.PRAGMA, "no-cache");
        response.setHeader(HttpHeaders.CACHE_CONTROL, "no-cache");
        response.setDateHeader(HttpHeaders.EXPIRES, 0);

        // 3. 关键：手动管理ZipOutputStream，不依赖try-with-resources提前关闭
        ZipOutputStream zipOut = null;
        try {
            // 初始化ZipOutputStream（直接关联响应流，全程不被包装流关闭）
            zipOut = new ZipOutputStream(response.getOutputStream());
            // 处理ZIP文件名（兼容所有浏览器）
            String zipFileName = "多CSV文件_" + System.currentTimeMillis() + ".zip";
            String encodedFileName = URLEncoder.encode(zipFileName, "UTF-8").replaceAll("\\+", "%20");
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
                    "attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName);

            // 4. 写入多个CSV（核心：每次写入后仅关闭ZIP条目，不关闭流）
            addCsvToZip(zipOut, "用户数据.csv", userData);
            addCsvToZip(zipOut, "商品数据.csv", productData);
            addCsvToZip(zipOut, "订单数据.csv", orderData);

            zipOut.flush(); // 确保所有数据写入响应流
            log.info("多CSV打包成功，共3个文件");

        } catch (Exception e) {
            log.error("多CSV打包失败", e);
            // 异常时重置响应，避免返回损坏的ZIP文件
            try {
                response.reset();
                response.setContentType("text/plain; charset=UTF-8");
                response.getWriter().write("文件导出失败，请稍后重试");
            } catch (Exception ex) {
                log.error("错误信息写入响应流失败", ex);
            }
        } finally {
            // 5. 最终关闭ZipOutputStream（所有写入完成后，仅关闭一次）
            if (zipOut != null) {
                try {
                    zipOut.close();
                } catch (IOException e) {
                    log.error("ZipOutputStream关闭失败", e);
                }
            }
        }
    }


    /**
     * 核心优化：不关闭CSVWriter和BufferedWriter，仅用其写入能力
     */
    private void addCsvToZip(ZipOutputStream zipOut, String csvFileName, List<String[]> data) throws IOException {
        // 1. 创建当前CSV的ZIP条目（每个CSV独立成一个条目）
        ZipEntry zipEntry = new ZipEntry(csvFileName);
        zipOut.putNextEntry(zipEntry);

        // 2. 构建流时不使用try-with-resources，避免自动关闭
        // 注意：此处不手动关闭任何流，仅在ZipOutputStream最终关闭时一并释放
        OutputStreamWriter osw = new OutputStreamWriter(zipOut, StandardCharsets.UTF_8);
        BufferedWriter bufferedWriter = new BufferedWriter(osw);
        CSVWriter writer = new CSVWriter(bufferedWriter);

        try {
            // 写入UTF-8 BOM头（解决Excel乱码），必须flush确保先写入
            bufferedWriter.write('\uFEFF');
            bufferedWriter.flush();

            // 保留核心：用CSVWriter.writeAll批量写入数据
            writer.writeAll(data);
            writer.flush(); // 仅刷新数据到ZipOutputStream，不触发关闭
            bufferedWriter.flush(); // 二次刷新，确保无数据残留
        } finally {
            // 3. 关键：不关闭writer/bufferedWriter/osw，仅关闭当前ZIP条目
            // （这些流会在ZipOutputStream最终关闭时自动释放，无需手动处理）
            zipOut.closeEntry();
        }
    }



    public static void mainOpenCsv() {
        // 导出路径
        String filePath = "D:/users_opencsv.csv";

        // 模拟数据（每行数据用字符串数组表示）
        List<String[]> data = new ArrayList<>();
        data.add(new String[]{"姓名", "年龄", "", "性别"}); // 表头
        data.add(new String[]{"张三", "25", "zhangsan@example.com", "男"});
        data.add(new String[]{"李四", "30", "lisi@example.com", "女"});
        data.add(new String[]{"王五", "28", "wangwu@example.com", "男"});
        // 测试特殊字符（OpenCSV 会自动处理逗号和引号）
        data.add(new String[]{"赵六", "35", "zhaoliu@example.com", "男,女"}); // 包含逗号

        // 写入文件
        try (
                // 1. 通过 FileOutputStream 构建输出流
                FileOutputStream fos = new FileOutputStream(filePath);
                // 2. 用 OutputStreamWriter 指定编码为 UTF-8（关键步骤）
                OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
                // 3. 将带编码的字符流传入 CSVWriter
                CSVWriter writer = new CSVWriter(osw)

        ) {
            // 写入 UTF-8 BOM 头（关键：让 Excel 识别 UTF-8）
            osw.write('\uFEFF');
            writer.writeAll(data); // 批量写入所有行
            System.out.println("CSV 文件导出成功，路径：" + filePath);
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("CSV 文件导出失败：" + e.getMessage());
        }
    }


    public static void main(String[] args) {

        // mainOpenCsv();
        // 初始化数据（表头 + 内容）
        List<Object[]> data = new ArrayList<>();
        Integer email = null;
        Integer email2 = 25;
        BigDecimal money = new BigDecimal("2.00");
        data.add(new Object[]{"姓名", "年龄", email, "性别"}); // 表头
        data.add(new Object[]{null, "25", email2, "男"});
        data.add(new Object[]{"", "30", money, "女"});
        data.add(new Object[]{"王五", 28, "wangwu@example.com", "男"});

        // 转换为指定格式字符串并输出
        String csvFormatStr = convertToCsvFormat(data);
        System.out.println(csvFormatStr);
    }

    /**
     * 将 List<String[]> 转换为 CSV 格式字符串（字段用双引号包裹，逗号分隔）
     *
     * @param data 待转换的数据（第一个元素为表头）
     * @return 格式化后的字符串
     */
    public static String convertToCsvFormat(List<Object[]> data) {
        StringBuilder sb = new StringBuilder();
        // 遍历所有行（包括表头）
        for (Object[] row : data) {
            // 处理每行的每个字段
            for (int i = 0; i < row.length; i++) {
                Object objResult = row[i];
                if (null != objResult) {
                    if (objResult instanceof BigDecimal || objResult instanceof Integer) {
                        sb.append(objResult);
                    } else {
                        //默认字符串
                        String result = String.valueOf(objResult);
                        if (StringUtils.isNotBlank(result)) {
                            sb.append("\"").append(result).append("\""); // 字段用双引号包裹
                        }
                    }
                }
                // 除最后一个字段外，添加逗号分隔
                if (i != row.length - 1) {
                    sb.append(",");
                }
            }
            sb.append("\n"); // 每行结束后换行
        }
        return sb.toString();
    }
}

